package junit;

import matrix.DimensionException;
import matrix.Matrix;
import org.junit.*;
import org.hamcrest.*;
import org.junit.rules.ExpectedException;


import java.io.FileReader;
import java.io.IOException;

public class TestMatrix extends Assert{

    private static final Matrix[] input = new  Matrix[5];

    @Before
    public void setUpInput(){
        try {
            input[0] = new Matrix(new double[][] {{1, 0, 0},{0, 1, 0},{0, 0, 1}});
            input[1] = new Matrix(new double[][] {{-1, 5, 8},{0, 1, 3},{0, 0, -7}});
            input[2] = new Matrix(new double[][] {{0, 0},{0, 0},{0, 0}});
            input[3] = new Matrix(new double[][] {{-2,2,-3},{-1,1,3},{2,0,-1}});
            input[4] = new Matrix(new double[][] {{1, 0, 0},{0, 1, 0},{0, 0, 1}});
        }catch (DimensionException dex){System.out.println(dex);}
    }

    @Rule
    public ExpectedException thrown = ExpectedException.none();

    @Test
    public void testConstructor () throws DimensionException{
        Matrix m1 = new Matrix(3, 2);
        assertEquals("Failure - constructor 01 doesn't work", m1.toString(), input[2].toString());
        try {
            assertEquals("Failure - constructor 02 doesn't work", -1, input[1].get(0, 0), 0.001);
            assertEquals("Failure - constructor 02 doesn't work", 5, input[1].get(0, 1), 0.001);
        } catch (DimensionException dex){System.out.println(dex);}

        thrown.expect(DimensionException.class);
        thrown.expectMessage("Number of elements in row is different");
        new Matrix( new double[][] {{1,1,1},{1,1}});
    }

    @Test
    public void toStringTest (){
        assertEquals(input[0].toString(),"\n1.0 0.0 0.0 \n0.0 1.0 0.0 \n0.0 0.0 1.0 ");
    }

    @Test
    public void setGetTest() {
        try {
            assertEquals("Error in Get method", input[1].get(0, 1), 5, 0.0001);
            input[1].set(0, 1, -5);
            assertEquals("Error in Set method", input[1].get(0, 1), -5, 0.0001);
        } catch (Exception ex) {
            System.out.println(ex);
        }

        try {
            input[0].set(1, 5, 1);
            fail("Set method exception isn't thrown");
        } catch (DimensionException dex) {
            assertEquals(dex.getMessage(), "Out of matrix size. You try to set [1] [5] element, but matrix is [3] [3]");
        }
        try {
            input[0].get(1, 5);
            fail("Get method exception isn't thrown");
        } catch (DimensionException dex) {
            assertEquals(dex.getMessage(), "No such element [1] [5],because matrix is [3] [3]");
        }
    }

    @Test
    public void diagonalTest() {
        try {
            assertTrue("isDiagonal method don't know diagonal matrix", input[0].isDiagonal());
            assertFalse("isDiagonal method don't know non diagonal matrix", input[1].isDiagonal());
        } catch (DimensionException dex) {
            System.out.println(dex);
        }
        try {
            input[2].isDiagonal();
            fail("isDiagonal method exception isn't thrown");
        } catch (DimensionException dex) {
            assertEquals(dex.getMessage(), "matrix isn't square");
        }
    }

    @Test
    public void detTest(){
        try {
            assertEquals("Determinant of 2x2 matrix is incorrect", 1, Matrix.det(new Matrix(new double[][]{{1, 5}, {0, 1}})), 0.0001);
            assertEquals("Determinant of 3x3 matrix is incorrect", 18, Matrix.det(input[3]), 0.0001);
        } catch (DimensionException dex) {
            System.out.println(dex);
        }
        try {
            Matrix.det(input[2]);
        } catch (DimensionException dex) {
            assertEquals(dex.getMessage(), "matrix isn't square");
        }
    }

    @Test
    public void fileReadingTest() {

        try {
            Matrix expected = new Matrix(new double[][] {{1, 0, 4}, {-3, 12, 0.001}, {4, -0.17, 0}});
            Matrix m = Matrix.readMatrixFromFile(new FileReader("src/junit/m1.txt"));
            assertTrue("Reading is incorrect", m.equals(expected));
        } catch (Exception ex) {
            System.out.println(ex);
        }
        try {
            Matrix.readMatrixFromFile(new FileReader("src/junit/m2.txt"));
            fail("FileReading method exception isn't thrown in case of incorrect length of rows");
        } catch (Exception ex) {
            assertEquals(ex.getMessage(), "Number of elements in row is different");
        }
        try {
            Matrix.readMatrixFromFile(new FileReader("src/junit/m3.txt"));
            fail("FileReading method exception isn't thrown in case of text in file");
        } catch (Exception ex) {
            assertEquals(ex.getMessage(), "File data can't be converted to the numbers");
        }
    }

    @Test
    public void getInvertibleTest(){
        try {
            Matrix invert = input[3].getInvertible();
            assertTrue(input[0].equals(input[3].multiplicate(invert)));
        }catch (DimensionException dex){System.out.println(dex);}
    }

    @Test
    public void multiplicateTest(){
        try {
            Matrix expected = new Matrix(new double[][] {{13, 3, 10}, {5, 1, 0}, {-14, 0, 7}});
            assertTrue("Multiplication is wrong", input[1].multiplicate(input[3]).equals(expected));
        } catch (DimensionException dex) {
            System.out.println(dex);
        }
        try {
            input[2].multiplicate(input[1]);
            fail("Multiplicate method exception isn't thrown");
        } catch (DimensionException dex) {
            assertEquals(dex.getMessage(), "matrix does not satisfy the criterion of compatibility");
        }
    }
}
